using System;
using InfiniTec.Data;
using InfiniTec.Properties;

namespace InfiniTec.Expressions
{
    public abstract class Expression
    {
        public string ToString(IExpressionFormatter formatter)
        {
            if (formatter == null) throw new ArgumentNullException("formatter");

            return formatter.FormatExpression(this);
        }

        public abstract bool Evaluate(PropertyCollection properties);


        public static Expression And(params Expression[] expressions)
        {
            return new AndExpression(expressions);
        }

        public static Expression Or(params Expression[] expressions)
        {
            return new OrExpression(expressions);
        }

        public static Expression Not(Expression expression)
        {
            return new NotExpression(expression);
        }

        public static Expression Equals<T>(PropertyDefinition<T> property, T value)
        {
            return new CompareExpression<T>(property, CompareType.Equals, value);
        }
        public static Expression NotEquals<T>(PropertyDefinition<T> property, T value)
        {
            return new CompareExpression<T>(property, CompareType.NotEquals, value);
        }
        public static Expression Smaller<T>(PropertyDefinition<T> property, T value)
        {
            return new CompareExpression<T>(property, CompareType.Smaller, value);
        }
        public static Expression SmallerOrEqual<T>(PropertyDefinition<T> property, T value)
        {
            return new CompareExpression<T>(property, CompareType.SmallerOrEqual, value);
        }
        public static Expression Greater<T>(PropertyDefinition<T> property, T value)
        {
            return new CompareExpression<T>(property, CompareType.Greater, value);
        }
        public static Expression GreaterOrEqual<T>(PropertyDefinition<T> property, T value)
        {
            return new CompareExpression<T>(property, CompareType.GreaterOrEqual, value);
        }

        public static Expression Equals<T>(PropertyDefinition<T?> property, T value)
            where T : struct
        {
            return new CompareExpression<T?>(property, CompareType.Equals, value);
        }
        public static Expression NotEquals<T>(PropertyDefinition<T?> property, T value)
            where T : struct
        {
            return new CompareExpression<T?>(property, CompareType.NotEquals, value);
        }
        public static Expression Smaller<T>(PropertyDefinition<T?> property, T value)
            where T : struct
        {
            return new CompareExpression<T?>(property, CompareType.Smaller, value);
        }
        public static Expression SmallerOrEqual<T>(PropertyDefinition<T?> property, T value)
            where T : struct
        {
            return new CompareExpression<T?>(property, CompareType.SmallerOrEqual, value);
        }
        public static Expression Greater<T>(PropertyDefinition<T?> property, T value)
            where T : struct
        {
            return new CompareExpression<T?>(property, CompareType.Greater, value);
        }
        public static Expression GreaterOrEqual<T>(PropertyDefinition<T?> property, T value)
            where T : struct
        {
            return new CompareExpression<T?>(property, CompareType.GreaterOrEqual, value);
        }

        public static Expression Like(PropertyDefinition<string> property, string value)
        {
            return new LikeExpression(property, value);
        }

        public static Expression Between<T>(PropertyDefinition<T> property, T lowerValue, T upperValue) 
            where T: struct
        {
            return new BetweenExpression<T>(property, lowerValue, upperValue, BetweenBoundaryInclusion.Both);
        }

        public static Expression Between<T>(PropertyDefinition<T> property, T lowerValue, T upperValue, BetweenBoundaryInclusion boundaryInclusion)
            where T : struct
        {
            return new BetweenExpression<T>(property, lowerValue, upperValue, boundaryInclusion);
        }

        public static Expression operator +(Expression a, Expression b)
        {
            if (a == null && b == null) throw new ArgumentNullException("a", Resources.ArgumentNullException_ExpressionMustBeNotNull);

            if (a == null) return b;
            if (b == null) return a;

            return And(a, b);
        }

        public static Expression operator *(Expression a, Expression b)
        {
            if (a == null && b == null) throw new ArgumentNullException("a", Resources.ArgumentNullException_ExpressionMustBeNotNull);

            if (a == null) return b;
            if (b == null) return a;

            return Or(a, b);
        }

    }
}
